/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.kk.app.render;

import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.opengl.EGL14;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.Matrix;
import android.util.Log;
import android.view.Surface;

import com.kk.app.utils.CollectionsHelper;
import com.kk.app.utils.IPredicate;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;

/**
 * A renderer class that manages the GL state, and can draw a frame into a set of output
 * {@link Surface}s.
 */
public class SurfaceTextureRenderer {
    private static final String TAG = SurfaceTextureRenderer.class.getSimpleName();
    private static final boolean DEBUG = false;
    private static final int EGL_RECORDABLE_ANDROID = 0x3142; // from EGL/eglext.h
    private static final int GL_MATRIX_SIZE = 16;
    private static final int VERTEX_POS_SIZE = 3;
    private static final int VERTEX_UV_SIZE = 2;
    private static final int EGL_COLOR_BITLENGTH = 8;
    private static final int GLES_VERSION = 2;
    private static final int PBUFFER_PIXEL_BYTES = 4;

    private static final int FLIP_TYPE_NONE = 0;
    private static final int FLIP_TYPE_HORIZONTAL = 1;
    private static final int FLIP_TYPE_VERTICAL = 2;
    private static final int FLIP_TYPE_BOTH = FLIP_TYPE_HORIZONTAL | FLIP_TYPE_VERTICAL;

    private EGLDisplay mEGLDisplay = EGL10.EGL_NO_DISPLAY;
    private EGLContext mEGLContext = EGL10.EGL_NO_CONTEXT;
    private EGLConfig mConfigs;

    private EGLContext mSharedEglContext = EGL10.EGL_NO_CONTEXT;


    private class EGLSurfaceHolder {
        long key;
        Surface surface;
        EGLSurface eglSurface;
        int width;
        int height;
        int sType = 0;
        int sMaxscreen=4;
        boolean valid = true;
    }

    private List<EGLSurfaceHolder> mSurfaces = new ArrayList<EGLSurfaceHolder>();

    // Hold this to avoid GC
    private volatile SurfaceTexture mSurfaceTexture;


    private static final int FLOAT_SIZE_BYTES = 4;
    private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
    private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
    private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;

    // Sampling is mirrored across the horizontal axis
    private static final float[] sHorizontalFlipTriangleVertices = {
            // X, Y, Z, U, V
            -1.0f, -1.0f, 0, 1.f, 0.f,
            1.0f, -1.0f, 0, 0.f, 0.f,
            -1.0f,  1.0f, 0, 1.f, 1.f,
            1.0f,  1.0f, 0, 0.f, 1.f,
    };

    // Sampling is mirrored across the vertical axis
    private static final float[] sVerticalFlipTriangleVertices = {
            // X, Y, Z, U, V
            -1.0f, -1.0f, 0, 0.f, 1.f,
            1.0f, -1.0f, 0, 1.f, 1.f,
            -1.0f,  1.0f, 0, 0.f, 0.f,
            1.0f,  1.0f, 0, 1.f, 0.f,
    };

    // Sampling is mirrored across the both axes
    private static final float[] sBothFlipTriangleVertices = {
            // X, Y, Z, U, V
            -1.0f, -1.0f, 0, 1.f, 1.f,
            1.0f, -1.0f, 0, 0.f, 1.f,
            -1.0f,  1.0f, 0, 1.f, 0.f,
            1.0f,  1.0f, 0, 0.f, 0.f,
    };

    // Sampling is 1:1 for a straight copy for the back camera
    private static final float[] sRegularTriangleVertices = {
            // X, Y, Z, U, V
            -1.0f, -1.0f, 0, 0.f, 0.f,
            1.0f, -1.0f, 0, 1.f, 0.f,
            -1.0f,  1.0f, 0, 0.f, 1.f,
            1.0f,  1.0f, 0, 1.f, 1.f,
    };

    //前缀（prefix）、后缀（suffix）

    /**
     * 裁剪左边1/4，修改的是右边结束位置
     */
    private static final float[] sRegularTriangleVertices_OneFour_Pre = {
            // X, Y, Z, U, V
            //左下角
//            -1.0f, -1.0f, 0, 0.f, 0.f,
//            //右下角
//            1.0f, -1.0f, 0, 1.0f/4.0f, 0.f,
//            //左上角
//            -1.0f,  1.0f, 0, 0.f, 1.f,
//            //右上角
//            1.0f,  1.0f, 0, 1.0f/4.0f, 1.f,

            // X, Y, Z, U, V
            -1.0f, -1.0f, 0, 1/3.f, 0.f,
            1.0f, -1.0f, 0, 0.f, 0.f,
            -1.0f,  1.0f, 0, 1/3.f, 1.f,
            1.0f,  1.0f, 0, 0.f, 1.f,

//            -1.0f, -1.0f, 0, 3.0f/4.0f, 0.f,
//            1.0f, -1.0f, 0, 1.0f, 0.f,
//            -1.0f,  1.0f, 0, 3.0f/4.0f, 1.f,
//            1.0f,  1.0f, 0, 1.0f, 1.f,
    };

    /**
     * 裁剪右边1/4,修改的是左边开始位置
     */
    private static final float[] sRegularTriangleVertices_OneFour_Suf = {
            // X, Y, Z, U, V
//            //左下角
//            -1.0f, -1.0f, 0, 3.0f/4.0f, 0.f,
//            //右下角
//            1.0f, -1.0f, 0, 1.f, 0.f,
//            //左上角
//            -1.0f,  1.0f, 0, 3.0f/4.0f, 1.f,
//            //右上角
//            1.0f,  1.0f, 0, 1.f, 1.f,

            -1.0f, -1.0f, 0, 1.f, 0.f,
            1.0f, -1.0f, 0, 2.0f/3.0f, 0.f,
            -1.0f,  1.0f, 0, 1.f, 1.f,
            1.0f,  1.0f, 0, 2.0f/3.0f, 1.f,
    };

    private FloatBuffer mRegularTriangleVertices;
    private FloatBuffer mHorizontalFlipTriangleVertices;
    private FloatBuffer mVerticalFlipTriangleVertices;
    private FloatBuffer mBothFlipTriangleVertices;
    private FloatBuffer mRegularTriangleVertices_OneFour_Pre;
    private FloatBuffer mRegularTriangleVertices_OneFour_Suf;
    private final int mFacing;

    /**
     * As used in this file, this vertex shader maps a unit square to the view, and
     * tells the fragment shader to interpolate over it.  Each surface pixel position
     * is mapped to a 2D homogeneous texture coordinate of the form (s, t, 0, 1) with
     * s and t in the inclusive range [0, 1], and the matrix from
     * {@link SurfaceTexture#getTransformMatrix(float[])} is used to map this
     * coordinate to a texture location.
     */
    private static final String VERTEX_SHADER =
            "precision mediump float;\n"+
            "uniform mat4 uMVPMatrix;\n" +
            "uniform mat4 uSTMatrix;\n" +
            "attribute vec4 aPosition;\n" +
            "attribute vec4 aTextureCoord;\n" +
            "varying vec2 vTextureCoord;\n" +
            "uniform int useGaussV;\n"+
            "const int SHIFT_SIZE = 2;\n"+
            "uniform highp float texelWidthOffset;\n"+
            "uniform highp float texelHeightOffset;\n"+
            "varying vec4 blurShiftCoordinates[SHIFT_SIZE];\n"+
            "void main() {\n" +
            "  gl_Position = uMVPMatrix * aPosition;\n" +
            "  vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
             "if(useGaussV>0){\n"+
                    "vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);\n"+
                    "for (int i = 0; i < SHIFT_SIZE; i++) {\n"+
                        "blurShiftCoordinates[i] = vec4(vTextureCoord.xy - float(i + 1) * singleStepOffset,\n"+
                        "vTextureCoord.xy + float(i + 1) * singleStepOffset);\n"+
                    "}\n"+
             "}\n"+
            "}\n";

    /**
     * This fragment shader simply draws the color in the 2D texture at
     * the location from the {@code VERTEX_SHADER}.
     */
//    private static final String FRAGMENT_SHADER =
//            "#extension GL_OES_EGL_image_external : require\n" +
//            "precision mediump float;\n" +
//            "varying vec2 vTextureCoord;\n" +
//            "uniform samplerExternalOES sTexture;\n" +
//            "const int SHIFT_SIZE = 2;\n"+
//            "varying vec4 blurShiftCoordinates[SHIFT_SIZE];\n"+
//            "varying int useGauss;\n"+
//            "void main() {\n" +
//                    "if(useGauss<=0){\n"+
//                     "gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
//                    "}else{\n"+
//                    "vec4 currentColor = texture2D(sTexture, vTextureCoord);\n"+
//                    "mediump vec3 sum = currentColor.rgb;\n"+
//                    "for (int i = 0; i < SHIFT_SIZE; i++) {\n"+
//                         "sum += texture2D(sTexture, blurShiftCoordinates[i].xy).rgb;\n"+
//                         "sum += texture2D(sTexture, blurShiftCoordinates[i].zw).rgb;\n"+
//                         "}\n"+
//                      "gl_FragColor = vec4(sum * 1.0 / float(2 * SHIFT_SIZE + 1), currentColor.a);\n"+
//                    "}\n"+
//            "}\n";

    private static final String FRAGMENT_SHADER =
            "#extension GL_OES_EGL_image_external : require\n" +
                    "precision mediump float;\n" +
                    "varying vec2 vTextureCoord;\n" +
                    "uniform samplerExternalOES sTexture;\n" +
                    "const int SHIFT_SIZE = 2;\n"+
                    "uniform int useGaussF;\n"+
                    "varying vec4 blurShiftCoordinates[SHIFT_SIZE];\n"+
                    "void main() {\n" +
                    "if(useGaussF<=0){\n"+
                     "gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
                    "}else{\n"+
                    "vec4 currentColor = texture2D(sTexture, vTextureCoord);\n"+
                    "mediump vec3 sum = currentColor.rgb;\n"+
                    "for (int i = 0; i < SHIFT_SIZE; i++) {\n"+
                         "sum += texture2D(sTexture, blurShiftCoordinates[i].xy).rgb;\n"+
                         "sum += texture2D(sTexture, blurShiftCoordinates[i].zw).rgb;\n"+
                         "}\n"+
                      "gl_FragColor = vec4(sum * 1.0 / float(2 * SHIFT_SIZE + 1), currentColor.a);\n"+
                    "}\n"+
                    "}\n";

    private float[] mMVPMatrix = new float[GL_MATRIX_SIZE];
    private float[] mSTMatrix = new float[GL_MATRIX_SIZE];

    private int mProgram;
    private int mTextureID = 0;
    private int muMVPMatrixHandle;
    private int muSTMatrixHandle;
    private int maPositionHandle;
    private int maTextureHandle;


    //高斯模糊
    private int mTexelWidthOffsetHandle;
    private int mTexelHeightOffsetHandle;
    private int mUseGaussVHandle = 0;
    private int mUseGaussFHandle = 0;
    private final float mBlurSize = 0.8f;
//    private final float mBlurSize = 1.0f;

    private int mTextureWidth = 1280;
    private int mTextureHeight = 720;

    /*
      准备的解码宽和高
     */
    private int mVideoBufferWidth = 1280;
    private int mVideoBufferHeight = 720;
    /**
     * 解码成功的宽和高
     */
    private int mVideoBufferWidth1 = 1280;
    private int mVideoBufferHeight1 = 720;

    private boolean mDrawVideo = true;

    EGL10 mEgl;

    public void setDrawVideo(boolean drawVideo) {
        mDrawVideo = drawVideo;
    }

    /**
     * prepare的获取的时候的texture
     * @param texWidth
     * @param texHeight
     * @param decoderW
     * @param decoderH
     */
    public void updateTextureSizePrepare(int texWidth, int texHeight, int decoderW, int decoderH)
    {
        if(texWidth>0)
        {
            mTextureWidth = texWidth;
        }
        if(texHeight>0)
        {
            mTextureHeight = texHeight;
        }
        if(decoderW>0)
        {
            mVideoBufferWidth = decoderW;
        }

        if(decoderH>0)
        {
            mVideoBufferHeight=decoderH;
        }
        Log.i(TAG, "updateTextureSizePrepare: w:"+mTextureWidth +" h:"+mTextureHeight);
    }

    /**
     * prepare的获取的时候的texture
     * @param texWidth
     * @param texHeight
     * @param decoderW
     * @param decoderH
     */
    public void updateTextureSizeRenderStart(int texWidth, int texHeight, int decoderW, int decoderH)
    {
        if(texWidth>0)
        {
            mTextureWidth = texWidth;
        }
        if(texHeight>0)
        {
            mTextureHeight = texHeight;
        }
        if(decoderW>0)
        {
            mVideoBufferWidth1 = decoderW;
        }

        if(decoderH>0)
        {
            mVideoBufferHeight1=decoderH;
        }
        Log.i(TAG, "updateTextureSizePrepare: w:"+mTextureWidth +" h:"+mTextureHeight);
    }

    private MVRenderData mMvRenderData = null;

    public SurfaceTextureRenderer(int facing, MVRenderData mvRenderData) {
        mFacing = facing;
        mMvRenderData = mvRenderData;

        mRegularTriangleVertices = ByteBuffer.allocateDirect(sRegularTriangleVertices.length *
                FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
        mRegularTriangleVertices.put(sRegularTriangleVertices).position(0);

        mRegularTriangleVertices_OneFour_Pre = ByteBuffer.allocateDirect(sRegularTriangleVertices_OneFour_Pre.length *
                FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
        mRegularTriangleVertices_OneFour_Pre.put(sRegularTriangleVertices_OneFour_Pre).position(0);

        mRegularTriangleVertices_OneFour_Suf = ByteBuffer.allocateDirect(sRegularTriangleVertices_OneFour_Suf.length *
                FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
        mRegularTriangleVertices_OneFour_Suf.put(sRegularTriangleVertices_OneFour_Suf).position(0);

        mHorizontalFlipTriangleVertices = ByteBuffer.allocateDirect(
                sHorizontalFlipTriangleVertices.length * FLOAT_SIZE_BYTES).
                order(ByteOrder.nativeOrder()).asFloatBuffer();
        mHorizontalFlipTriangleVertices.put(sHorizontalFlipTriangleVertices).position(0);

        mVerticalFlipTriangleVertices = ByteBuffer.allocateDirect(
                sVerticalFlipTriangleVertices.length * FLOAT_SIZE_BYTES).
                order(ByteOrder.nativeOrder()).asFloatBuffer();
        mVerticalFlipTriangleVertices.put(sVerticalFlipTriangleVertices).position(0);

        mBothFlipTriangleVertices = ByteBuffer.allocateDirect(
                sBothFlipTriangleVertices.length * FLOAT_SIZE_BYTES).
                order(ByteOrder.nativeOrder()).asFloatBuffer();
        mBothFlipTriangleVertices.put(sBothFlipTriangleVertices).position(0);

        Matrix.setIdentityM(mSTMatrix, 0);
        mEgl = (EGL10) EGLContext.getEGL();
    }

    private int loadShader(int shaderType, String source) {
        int shader = GLES20.glCreateShader(shaderType);
        checkGlError("glCreateShader type=" + shaderType);
        GLES20.glShaderSource(shader, source);
        GLES20.glCompileShader(shader);
        int[] compiled = new int[1];
        GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
        if (compiled[0] == 0) {
            Log.e(TAG, "Could not compile shader " + shaderType + ":");
            Log.e(TAG, " " + GLES20.glGetShaderInfoLog(shader));
            GLES20.glDeleteShader(shader);
            // TODO: handle this more gracefully
            throw new IllegalStateException("Could not compile shader " + shaderType);
        }
        return shader;
    }

    private int createProgram(String vertexSource, String fragmentSource) {
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
        if (vertexShader == 0) {
            return 0;
        }
        int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
        if (pixelShader == 0) {
            return 0;
        }

        int program = GLES20.glCreateProgram();
        checkGlError("glCreateProgram");
        if (program == 0) {
            Log.e(TAG, "Could not create program");
        }
        GLES20.glAttachShader(program, vertexShader);
        checkGlError("glAttachShader");
        GLES20.glAttachShader(program, pixelShader);
        checkGlError("glAttachShader");
        GLES20.glLinkProgram(program);
        int[] linkStatus = new int[1];
        GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
        if (linkStatus[0] != GLES20.GL_TRUE) {
            Log.e(TAG, "Could not link program: ");
            Log.e(TAG, GLES20.glGetProgramInfoLog(program));
            GLES20.glDeleteProgram(program);
            // TODO: handle this more gracefully
            throw new IllegalStateException("Could not link program");
        }
        return program;
    }

    /**
     * 在这个4x4纹理坐标转换矩阵中，每个值代表了不同的含义和作用：
     *
     * 1. `mtx[0]`、`mtx[1]`、`mtx[2]`、`mtx[3]`：
     *    - `mtx[0]` 控制了X轴的缩放因子。
     *    - `mtx[1]` 控制了X轴的旋转和切变。
     *    - `mtx[2]` 控制了X轴的透视投影。
     *    - `mtx[3]` 控制了X轴的平移。
     *
     * 2. `mtx[4]`、`mtx[5]`、`mtx[6]`、`mtx[7]`：
     *    - `mtx[4]` 控制了Y轴的缩放因子。
     *    - `mtx[5]` 控制了Y轴的旋转和切变。
     *    - `mtx[6]` 控制了Y轴的透视投影。
     *    - `mtx[7]` 控制了Y轴的平移。
     *
     * 3. `mtx[8]`、`mtx[9]`、`mtx[10]`、`mtx[11]`：
     *    - `mtx[8]` 控制了Z轴的缩放因子。
     *    - `mtx[9]` 控制了Z轴的旋转和切变。
     *    - `mtx[10]` 控制了Z轴的透视投影。
     *    - `mtx[11]` 控制了Z轴的平移。
     *
     * 4. `mtx[12]`、`mtx[13]`、`mtx[14]`、`mtx[15]`：
     *    - `mtx[12]` 控制了齐次坐标系统中的X轴平移。
     *    - `mtx[13]` 控制了齐次坐标系统中的Y轴平移。
     *    - `mtx[14]` 控制了齐次坐标系统中的Z轴平移。
     *    - `mtx[15]` 通常设置为1，以保持齐次坐标的齐次性。
     *
     * 这个矩阵的目的是将一个坐标空间中的点映射到另一个坐标空间中，通常用于纹理映射和渲染中，以确保正确的纹理采样和渲染效果。不同的值会影响映射的方式，例如缩放、旋转、切变、透视和平移。这些变换可以用来实现各种图形效果和投影。
     * @param st
     * @param width
     * @param height
     * @param flipType
     * @throws LegacyExceptionUtils.BufferQueueAbandonedException
     */
    private void drawFrame(SurfaceTexture st, int width, int height, int flipType)
            throws LegacyExceptionUtils.BufferQueueAbandonedException {
        checkGlError("onDrawFrame start");
        st.getTransformMatrix(mSTMatrix);



        //Log.i(TAG, "drawFrame: tW:"+mTextureWidth+" th:"+mTextureHeight +" dw:"+mVideoBufferWidth1 +"  dh:"+mVideoBufferHeight1+ "matrix[0]:"+mSTMatrix[0]  +"  matrix[5]:"+mSTMatrix[5]+"  matrix[12]:"+mSTMatrix[12]+"  matrix[13]:"+mSTMatrix[13]+"  str:"+Arrays.toString(mSTMatrix));
//        if (mTextureWidth > 0 && mVideoDecoderWidth > 0 && mTextureWidth != mVideoDecoderWidth && Math.abs(mSTMatrix[0])==1) {
//            int fh = mSTMatrix[0] > 0 ? 1 : -1;
//            mSTMatrix[0] = (mTextureWidth - 2) * 1.0f * fh / mVideoDecoderWidth;
//        }
//        if (mTextureHeight > 0 && mVideoDecoderHeight > 0 && mTextureHeight != mVideoDecoderHeight&&Math.abs(mSTMatrix[5])==1)
//        {
//            int fh = mSTMatrix[5] > 0 ? 1 : -1;
//            mSTMatrix[5] = (mTextureHeight - 2) * 1.0f * fh / mVideoDecoderHeight;
//            mSTMatrix[13] = mTextureHeight * 1.0f / mVideoDecoderHeight;
//        }

        /**
         * 我们认为 从大到小的时候，不会有问题， 从小到大才可能出现灰变
         */
        if(mVideoBufferWidth1>mVideoBufferWidth)
        {

        }
        if(mVideoBufferHeight1>mVideoBufferHeight)
        {
            if(mSTMatrix[13]<1.0)
            {
                mSTMatrix[13] = 1.0f;
            }
        }


        Matrix.setIdentityM(mMVPMatrix, /*smOffset*/0);

//        try {
//            dimens = LegacyCameraDevice.getTextureSize(st);
//        } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
//            // Should never hit this.
//            throw new IllegalStateException("Surface abandoned, skipping drawFrame...", e);
//        }
        float texWidth = width;
        float texHeight = height;

        if (texWidth <= 0 || texHeight <= 0) {
            throw new IllegalStateException("Illegal intermediate texture with dimension of 0");
        }

        // Letterbox or pillar-box output dimensions into intermediate dimensions.
        RectF intermediate = new RectF(/*left*/0, /*top*/0, /*right*/texWidth, /*bottom*/texHeight);
        RectF output = new RectF(/*left*/0, /*top*/0, /*right*/width, /*bottom*/height);
        android.graphics.Matrix boxingXform = new android.graphics.Matrix();
        boxingXform.setRectToRect(output, intermediate, android.graphics.Matrix.ScaleToFit.CENTER);
        boxingXform.mapRect(output);

        // Find scaling factor from pillar-boxed/letter-boxed output dimensions to intermediate
        // buffer dimensions.
        float scaleX = intermediate.width() / output.width();
        float scaleY = intermediate.height() / output.height();

        // Intermediate texture is implicitly scaled to 'fill' the output dimensions in clip space
        // coordinates in the shader.  To avoid stretching, we need to scale the larger dimension
        // of the intermediate buffer so that the output buffer is actually letter-boxed
        // or pillar-boxed into the intermediate buffer after clipping.
        Matrix.scaleM(mMVPMatrix, /*offset*/0, /*x*/scaleX, /*y*/scaleY, /*z*/1);

        if (DEBUG) {
            Log.d(TAG, "Scaling factors (S_x = " + scaleX + ",S_y = " + scaleY + ") used for " +
                    width + "x" + height + " surface, intermediate buffer size is " + texWidth +
                    "x" + texHeight);
        }

        // Set viewport to be output buffer dimensions
        GLES20.glViewport(0, 0, width, height);

        if (DEBUG) {
            GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
            GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
        }

        GLES20.glUseProgram(mProgram);
        checkGlError("glUseProgram");

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);

        FloatBuffer triangleVertices;
        switch(flipType) {
            case FLIP_TYPE_HORIZONTAL:
                triangleVertices = mHorizontalFlipTriangleVertices;
                break;
            case FLIP_TYPE_VERTICAL:
                triangleVertices = mVerticalFlipTriangleVertices;
                break;
            case FLIP_TYPE_BOTH:
                triangleVertices = mBothFlipTriangleVertices;
                break;
            default:
                triangleVertices = mRegularTriangleVertices;
                break;
        }
        GLES20.glUniform1i(mUseGaussVHandle, 0);
        GLES20.glUniform1i(mUseGaussFHandle, 0);
        triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
        GLES20.glVertexAttribPointer(maPositionHandle, VERTEX_POS_SIZE, GLES20.GL_FLOAT,
                /*normalized*/ false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
        checkGlError("glVertexAttribPointer maPosition");
        GLES20.glEnableVertexAttribArray(maPositionHandle);
        checkGlError("glEnableVertexAttribArray maPositionHandle");

        triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
        GLES20.glVertexAttribPointer(maTextureHandle, VERTEX_UV_SIZE, GLES20.GL_FLOAT,
                /*normalized*/ false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
        checkGlError("glVertexAttribPointer maTextureHandle");
        GLES20.glEnableVertexAttribArray(maTextureHandle);
        checkGlError("glEnableVertexAttribArray maTextureHandle");

        GLES20.glUniformMatrix4fv(muMVPMatrixHandle, /*count*/ 1, /*transpose*/ false, mMVPMatrix,
                /*offset*/ 0);
        GLES20.glUniformMatrix4fv(muSTMatrixHandle, /*count*/ 1, /*transpose*/ false, mSTMatrix,
                /*offset*/ 0);

        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /*offset*/ 0, /*count*/ 4);
        checkGlDrawError("glDrawArrays");
    }


    /**
     *
     * @param st
     * @param width
     * @param height
     * @param type 0是没有， 1 是前面， 2是后面
     * @throws LegacyExceptionUtils.BufferQueueAbandonedException
     */
    private void drawFrameClip(SurfaceTexture st, int width, int height, int type, int screenMaxCount)
            throws LegacyExceptionUtils.BufferQueueAbandonedException {
        checkGlError("onDrawFrame start");
        if(screenMaxCount<=0)
            screenMaxCount = 1;
        st.getTransformMatrix(mSTMatrix);
//        if (mTextureWidth > 0 && mVideoDecoderWidth > 0 && mTextureWidth != mVideoDecoderWidth && Math.abs(mSTMatrix[0])==1) {
//            int fh = mSTMatrix[0] > 0 ? 1 : -1;
//            mSTMatrix[0] = (mTextureWidth - 2) * 1.0f * fh / mVideoDecoderWidth;
//        }
//        if (mTextureHeight > 0 && mVideoDecoderHeight > 0 && mTextureHeight != mVideoDecoderHeight&&Math.abs(mSTMatrix[5])==1) {
//            int fh = mSTMatrix[5] > 0 ? 1 : -1;
//            mSTMatrix[5] = (mTextureHeight - 2) * 1.0f * fh / mVideoDecoderHeight;
//            mSTMatrix[13] = mTextureHeight * 1.0f / mVideoDecoderHeight;
//        }
        if(mVideoBufferHeight1>mVideoBufferHeight)
        {
            if(mSTMatrix[13]<1.0)
            {
                mSTMatrix[13] = 1.0f;
            }
        }
        Matrix.setIdentityM(mMVPMatrix, /*smOffset*/0);

//        try {
//            dimens = LegacyCameraDevice.getTextureSize(st);
//        } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
//            // Should never hit this.
//            throw new IllegalStateException("Surface abandoned, skipping drawFrame...", e);
//        }
        float texWidth = width;
        float texHeight = height;

        if (texWidth <= 0 || texHeight <= 0) {
            throw new IllegalStateException("Illegal intermediate texture with dimension of 0");
        }

        // Letterbox or pillar-box output dimensions into intermediate dimensions.
        RectF intermediate = new RectF(/*left*/0, /*top*/0, /*right*/texWidth, /*bottom*/texHeight);
        RectF output = new RectF(/*left*/0, /*top*/0, /*right*/width, /*bottom*/height);
        android.graphics.Matrix boxingXform = new android.graphics.Matrix();
        boxingXform.setRectToRect(output, intermediate, android.graphics.Matrix.ScaleToFit.CENTER);
        boxingXform.mapRect(output);

        // Find scaling factor from pillar-boxed/letter-boxed output dimensions to intermediate
        // buffer dimensions.
        float scaleX = intermediate.width() / output.width();
        float scaleY = intermediate.height() / output.height();

        // Intermediate texture is implicitly scaled to 'fill' the output dimensions in clip space
        // coordinates in the shader.  To avoid stretching, we need to scale the larger dimension
        // of the intermediate buffer so that the output buffer is actually letter-boxed
        // or pillar-boxed into the intermediate buffer after clipping.
        Matrix.scaleM(mMVPMatrix, /*offset*/0, /*x*/scaleX, /*y*/scaleY, /*z*/1);

        if (DEBUG) {
            Log.d(TAG, "Scaling factors (S_x = " + scaleX + ",S_y = " + scaleY + ") used for " +
                    width + "x" + height + " surface, intermediate buffer size is " + texWidth +
                    "x" + texHeight);
        }
        GLES20.glViewport(0, 0, width, height);
        if (DEBUG) {
            GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
            GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
        }

        GLES20.glUseProgram(mProgram);
        checkGlError("glUseProgram");

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);

        FloatBuffer triangleVertices;
        switch (type)
        {
            case 1:
                triangleVertices = mRegularTriangleVertices_OneFour_Pre;
                break;
            case 2:
                triangleVertices = mRegularTriangleVertices_OneFour_Suf;
                break;
            default:
                triangleVertices = mRegularTriangleVertices;
                break;
        }
//        switch(flipType) {
//            case FLIP_TYPE_HORIZONTAL:
//                triangleVertices = mHorizontalFlipTriangleVertices;
//                break;
//            case FLIP_TYPE_VERTICAL:
//                triangleVertices = mVerticalFlipTriangleVertices;
//                break;
//            case FLIP_TYPE_BOTH:
//                triangleVertices = mBothFlipTriangleVertices;
//                break;
//            default:
//                triangleVertices = mRegularTriangleVertices;
//                break;
//        }

        if(type==0)
        {
            GLES20.glUniform1i(mUseGaussVHandle, 0);
            GLES20.glUniform1i(mUseGaussFHandle, 0);
        }
        else{
            GLES20.glUniform1i(mUseGaussVHandle, 1);
            GLES20.glUniform1i(mUseGaussFHandle, 1);
            if(mTextureWidth!=0)
            {
                GLES20.glUniform1f(mTexelWidthOffsetHandle,mBlurSize/mTextureWidth);
            }
            else{
                GLES20.glUniform1f(mTexelWidthOffsetHandle,0);
            }
            if(mTextureHeight!=0) {
                GLES20.glUniform1f(mTexelHeightOffsetHandle, mBlurSize / mTextureHeight);
            }
            else{
                GLES20.glUniform1f(mTexelHeightOffsetHandle,0);
            }
        }

        triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
        GLES20.glVertexAttribPointer(maPositionHandle, VERTEX_POS_SIZE, GLES20.GL_FLOAT,
                /*normalized*/ false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
        checkGlError("glVertexAttribPointer maPosition");
        GLES20.glEnableVertexAttribArray(maPositionHandle);
        checkGlError("glEnableVertexAttribArray maPositionHandle");

        triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
        GLES20.glVertexAttribPointer(maTextureHandle, VERTEX_UV_SIZE, GLES20.GL_FLOAT,
                /*normalized*/ false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
        checkGlError("glVertexAttribPointer maTextureHandle");
        GLES20.glEnableVertexAttribArray(maTextureHandle);
        checkGlError("glEnableVertexAttribArray maTextureHandle");

        GLES20.glUniformMatrix4fv(muMVPMatrixHandle, /*count*/ 1, /*transpose*/ false, mMVPMatrix,
                /*offset*/ 0);
        GLES20.glUniformMatrix4fv(muSTMatrixHandle, /*count*/ 1, /*transpose*/ false, mSTMatrix,
                /*offset*/ 0);

        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /*offset*/ 0, /*count*/ 4);
        checkGlDrawError("glDrawArrays");
    }
    /**
     * Initializes GL state.  Call this after the EGL surface has been created and made current.
     */
    private void initializeGLState() {
        mProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER);
        if (mProgram == 0) {
            throw new IllegalStateException("failed creating program");
        }
        maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
        checkGlError("glGetAttribLocation aPosition");
        if (maPositionHandle == -1) {
            throw new IllegalStateException("Could not get attrib location for aPosition");
        }
        maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
        checkGlError("glGetAttribLocation aTextureCoord");
        if (maTextureHandle == -1) {
            throw new IllegalStateException("Could not get attrib location for aTextureCoord");
        }

        muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
        checkGlError("glGetUniformLocation uMVPMatrix");
        if (muMVPMatrixHandle == -1) {
            throw new IllegalStateException("Could not get attrib location for uMVPMatrix");
        }

        muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix");
        checkGlError("glGetUniformLocation uSTMatrix");
        if (muSTMatrixHandle == -1) {
            throw new IllegalStateException("Could not get attrib location for uSTMatrix");
        }

        mTexelWidthOffsetHandle = GLES20.glGetUniformLocation(mProgram, "texelWidthOffset");
        mTexelHeightOffsetHandle = GLES20.glGetUniformLocation(mProgram, "texelHeightOffset");
        mUseGaussVHandle = GLES20.glGetUniformLocation(mProgram, "useGaussV");
        mUseGaussFHandle = GLES20.glGetUniformLocation(mProgram, "useGaussF");

        int[] textures = new int[1];
        GLES20.glGenTextures(/*n*/ 1, textures, /*offset*/ 0);

        mTextureID = textures[0];
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
        checkGlError("glBindTexture mTextureID");

        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
                GLES20.GL_LINEAR);
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
                GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
                GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
                GLES20.GL_CLAMP_TO_EDGE);
        checkGlError("glTexParameter");
    }

    private int getTextureId() {
        return mTextureID;
    }

    private void clearState() {
        mSurfaces.clear();
        if (mSurfaceTexture != null) {
            mSurfaceTexture.release();
        }
        mSurfaceTexture = null;
    }

    private void configureEGLContext() {
        mEGLDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
        if (mEGLDisplay == EGL10.EGL_NO_DISPLAY) {
            throw new IllegalStateException("No EGL10 display");
        }
        int[] version = new int[2];
        if (!mEgl.eglInitialize(mEGLDisplay, version)) {
            throw new IllegalStateException("Cannot initialize EGL10");
        }

        int[] attribList = {
                EGL10.EGL_RED_SIZE, EGL_COLOR_BITLENGTH,
                EGL10.EGL_GREEN_SIZE, EGL_COLOR_BITLENGTH,
                EGL10.EGL_BLUE_SIZE, EGL_COLOR_BITLENGTH,
                EGL10.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
                EGL_RECORDABLE_ANDROID, 1,
                EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT | EGL14.EGL_WINDOW_BIT,
                EGL14.EGL_NONE
        };
        EGLConfig[] configs = new EGLConfig[1];
        int[] numConfigs = new int[1];
        mEgl.eglChooseConfig(mEGLDisplay, attribList, /*offset*/ configs, /*offset*/
                configs.length, numConfigs);
        checkEglError("eglCreateContext RGB888+recordable ES2");
        mConfigs = configs[0];
        int[] attrib_list = {
                EGL14.EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION,
                EGL10.EGL_NONE
        };
        mEGLContext = mEgl.eglCreateContext(mEGLDisplay, configs[0], mSharedEglContext,
                attrib_list);
        Log.i(TAG, "configureEGLContext: SharedEglContext:" + mSharedEglContext);
        checkEglError("eglCreateContext");
        if (mEGLContext == EGL10.EGL_NO_CONTEXT) {
            throw new IllegalStateException("No EGLContext could be made");
        }
    }

    private void configureEGLOutputSurfaces(Collection<EGLSurfaceHolder> surfaces) {
        if (surfaces == null || surfaces.size() == 0) {
            throw new IllegalStateException("No Surfaces were provided to draw to");
        }
        int[] surfaceAttribs = {
                EGL14.EGL_NONE
        };
        for (EGLSurfaceHolder holder : surfaces) {
            holder.eglSurface = mEgl.eglCreateWindowSurface(mEGLDisplay, mConfigs,
                    holder.surface, surfaceAttribs);
            checkEglError("eglCreateWindowSurface");
        }
    }


    private void releaseEGLContext() {
        if (mEGLDisplay != EGL10.EGL_NO_DISPLAY) {
            mEgl.eglMakeCurrent(mEGLDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE,
                    EGL10.EGL_NO_CONTEXT);
            if (mSurfaces != null) {
                for (EGLSurfaceHolder holder : mSurfaces) {
                    if (holder.eglSurface != null) {
                        mEgl.eglDestroySurface(mEGLDisplay, holder.eglSurface);
                    }
                }
            }
            mEgl.eglDestroyContext(mEGLDisplay, mEGLContext);
            //mEgl.eglReleaseThread();
            mEgl.eglTerminate(mEGLDisplay);
        }

        mConfigs = null;
        mEGLDisplay = mEgl.EGL_NO_DISPLAY;
        mEGLContext = mEgl.EGL_NO_CONTEXT;
        clearState();
    }

    private void makeCurrent(EGLSurface surface) {
        mEgl.eglMakeCurrent(mEGLDisplay, surface, surface, mEGLContext);
        checkEglError("makeCurrent");
    }

    private boolean swapBuffers(EGLSurface surface)
            throws LegacyExceptionUtils.BufferQueueAbandonedException {
        boolean result = mEgl.eglSwapBuffers(mEGLDisplay, surface);
        int error = mEgl.eglGetError();
        if (error == mEgl.EGL_BAD_SURFACE) {
            Log.e(TAG, "swapBuffers: " + error);
            throw new LegacyExceptionUtils.BufferQueueAbandonedException();
        } else if (error != mEgl.EGL_SUCCESS) {
            Log.e(TAG, "swapBuffers: " + error);
            throw new IllegalStateException("swapBuffers: EGL error: 0x" +
                    Integer.toHexString(error));
        }
        return result;
    }

    private void checkEglError(String msg) {
        int error;
        if ((error = mEgl.eglGetError()) != EGL10.EGL_SUCCESS) {
            Log.e(TAG, "checkEglError: " + msg + "  " + error);
            throw new IllegalStateException(msg + ": EGL error: 0x" + Integer.toHexString(error));
        }
    }

    private void checkGlError(String msg) {
        int error;
        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
            Log.e(TAG, "checkGlError: " + msg + "  " + error);
            throw new IllegalStateException(
                    msg + ": GLES20 error: 0x" + Integer.toHexString(error));
        }
    }

    private void checkGlDrawError(String msg)
            throws LegacyExceptionUtils.BufferQueueAbandonedException {
        int error;
        boolean surfaceAbandoned = false;
        boolean glError = false;
        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
            Log.e(TAG, "checkGlDrawError: " + msg + " " + error);
            if (error == GLES20.GL_OUT_OF_MEMORY) {
                surfaceAbandoned = true;
            } else {
                glError = true;
            }
        }
        if (glError) {
            throw new IllegalStateException(
                    msg + ": GLES20 error: 0x" + Integer.toHexString(error));
        }
        if (surfaceAbandoned) {
            throw new LegacyExceptionUtils.BufferQueueAbandonedException();
        }
    }


    private void setupGlTiming() {
//        if (PerfMeasurement.isGlTimingSupported()) {
//            Log.d(TAG, "Enabling GL performance measurement");
//            mPerfMeasurer = new PerfMeasurement();
//        } else {
//            Log.d(TAG, "GL performance measurement not supported on this device");
//            mPerfMeasurer = null;
//        }
    }

    private void beginGlTiming() {
//        if (mPerfMeasurer == null) return;
//        mPerfMeasurer.startTimer();
    }

    private void addGlTimestamp(long timestamp) {
//        if (mPerfMeasurer == null) return;
//        mPerfMeasurer.addTimestamp(timestamp);
    }

    private void endGlTiming() {
//        if (mPerfMeasurer == null) return;
//        mPerfMeasurer.stopTimer();
    }

    /**
     * Return the surface texture to draw to - this is the texture use to when producing output
     * surface buffers.
     *
     * @return a {@link SurfaceTexture}.
     */
    public SurfaceTexture getSurfaceTexture() {
        return mSurfaceTexture;
    }


    public void delSurface(final long key)
    {
        Log.i(TAG, "delSurface: begin "+key);
        try{


            CollectionsHelper.RemoveIf(mSurfaces, new IPredicate<EGLSurfaceHolder>() {
                @Override
                public boolean test(EGLSurfaceHolder eglSurfaceHolder) {
                    if (eglSurfaceHolder != null && eglSurfaceHolder.key == key) {
                        eglSurfaceHolder.valid = false;
                        if (eglSurfaceHolder.eglSurface != null) {
                            mEgl.eglDestroySurface(mEGLDisplay, eglSurfaceHolder.eglSurface);
                        }
                        if (eglSurfaceHolder.surface != null) {
                            //eglSurfaceHolder.surface.release();
                            eglSurfaceHolder.surface = null;
                        }
                        return true;
                    }
                    return false;
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
        Log.i(TAG, "delSurface: end " + key);
    }

    public void addSurface(Surface surface, int w, int h, long key, int sType_, int maxScreen_) {
        if (surface == null)
            return;

        boolean add = true;
        for (int i = 0; i < mSurfaces.size(); i++) {
            if (mSurfaces.get(i).key == key && mSurfaces.get(i).surface == surface) {
                mSurfaces.get(i).width = w;
                mSurfaces.get(i).height = h;
                mSurfaces.get(i).sType = sType_;
                mSurfaces.get(i).sMaxscreen = maxScreen_;
                mSurfaces.get(i).valid = true;
                add = false;
                break;
            }
        }

        if (add) {
            try {
                EGLSurfaceHolder holder = new EGLSurfaceHolder();
                holder.surface = surface;
                holder.width = w;
                holder.height = h;
                holder.key = key;
                holder.sType = sType_;
                holder.sMaxscreen = maxScreen_;
                int[] surfaceAttribs = {
                        EGL14.EGL_NONE
                };
                holder.eglSurface = mEgl.eglCreateWindowSurface(mEGLDisplay, mConfigs,
                        holder.surface, surfaceAttribs);
                checkEglError("eglCreateWindowSurface");

                {
                    mSurfaces.add(holder);
                }
            } catch (Exception e) {
                Log.w(TAG, "Surface abandoned, skipping configuration... ", e);
            }
        }


        for (int i = 0; i < mSurfaces.size(); i++) {
            Log.i(TAG, "loop list " + mSurfaces.get(i).surface + "   key:" + mSurfaces.get(i).key);
        }


    }

    /**
     * Set a collection of output {@link Surface}s that can be drawn to.
     *
     * @param surfaces a {@link Collection} of surfaces.
     */
    public void configureSurfaces(Surface surface, int sw, int sh, long key) {
        releaseEGLContext();

//        if (surfaces == null || surfaces.size() == 0) {
//            Log.w(TAG, "No output surfaces configured for GL drawing.");
//            return;
//        }

        if (surface != null) {
            try {
                EGLSurfaceHolder holder = new EGLSurfaceHolder();
                holder.surface = surface;
                holder.width = sw;
                holder.height = sh;
                holder.key = key;
                {
                    mSurfaces.add(holder);
                }
            } catch (Exception e) {
                Log.w(TAG, "Surface abandoned, skipping configuration... ", e);
            }
        }

        // Set up egl display
        configureEGLContext();

        // Set up regular egl surfaces if needed
        if (mSurfaces.size() > 0) {
            configureEGLOutputSurfaces(mSurfaces);
        }


        // Set up pbuffer surface if needed

        if (mSurfaces.size() > 0) {
            makeCurrent(mSurfaces.get(0).eglSurface);
        } else {
            mEgl.eglMakeCurrent(mEGLDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, mEGLContext);
        }


        initializeGLState();
        mSurfaceTexture = new SurfaceTexture(getTextureId());

        if (mMvRenderData != null) {
            mMvRenderData.setTextureId(getTextureId());
            mMvRenderData.setSurfaceTexture(mSurfaceTexture);
        }
//        TestRender.setTextureId(getTextureId());
//        TestRender.setSurfaceTexture(mSurfaceTexture);

    }

    /**
     * Draw the current buffer in the {@link SurfaceTexture} returned from
     * {@link #getSurfaceTexture()} into the set of target {@link Surface}s
     * <p>
     * the frame if none is available.
     * <p>
     * Any {@link Surface}s targeted must be a subset of the {@link Surface}s
     * set in the last {@link #configureSurfaces(Collection)} call.
     * </p>
     *
     * @param targetCollector the surfaces to draw to.
     */
    public void drawIntoSurfaces(Object targetCollector) {

        if (mSurfaceTexture == null) {
            return;
        }

        mSurfaceTexture.updateTexImage();
        long timestamp = mSurfaceTexture.getTimestamp();


        for (EGLSurfaceHolder holder : mSurfaces) {
            {
                if (!holder.valid)
                    continue;
                try {

                    makeCurrent(holder.eglSurface);

//                    if (mDrawVideo)
                    {
                        if (holder.sType == 0) {
                            drawFrame(mSurfaceTexture, holder.width, holder.height,
                                    FLIP_TYPE_NONE);
                        } else {
                            drawFrameClip(mSurfaceTexture, holder.width, holder.height,
                                    holder.sType, holder.sMaxscreen);
                        }
                    }
//                    else {
//                        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
//                        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
//                    }
                    swapBuffers(holder.eglSurface);
                } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
                    Log.w(TAG, "Surface abandoned, dropping frame. " + holder.key);
                    if (holder != null) {
                        holder.valid = false;
                    }
                } catch (Exception e) {
                    if (e != null && e.getMessage() != null) {
                        Log.w(TAG, "" + e.getMessage() + "hold:" + holder.key);
                    }
                }
            }
        }


//        if ((mSurfaces == null || mSurfaces.size() == 0)
//                && (mConversionSurfaces == null || mConversionSurfaces.size() == 0)) {
//            return;
//        }
//
//        boolean doTiming = targetCollector.hasPendingPreviewCaptures();
//        checkGlError("before updateTexImage");
//
//        if (doTiming) {
//            beginGlTiming();
//        }
//
//        mSurfaceTexture.updateTexImage();
//
//        long timestamp = mSurfaceTexture.getTimestamp();
//
//        Pair<RequestHolder, Long> captureHolder = targetCollector.previewCaptured(timestamp);
//
//        // No preview request queued, drop frame.
//        if (captureHolder == null) {
//            if (DEBUG) {
//                Log.d(TAG, "Dropping preview frame.");
//            }
//            if (doTiming) {
//                endGlTiming();
//            }
//            return;
//        }
//
//        RequestHolder request = captureHolder.first;
//
//        Collection<Surface> targetSurfaces = request.getHolderTargets();
//        if (doTiming) {
//            addGlTimestamp(timestamp);
//        }
//
//        List<Long> targetSurfaceIds = new ArrayList();
//        try {
//            targetSurfaceIds = LegacyCameraDevice.getSurfaceIds(targetSurfaces);
//        } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
//            Log.w(TAG, "Surface abandoned, dropping frame. ", e);
//            request.setOutputAbandoned();
//        }
//
//        for (EGLSurfaceHolder holder : mSurfaces) {
//            if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
//                try{
//                    LegacyCameraDevice.setSurfaceDimens(holder.surface, holder.width,
//                            holder.height);
//                    makeCurrent(holder.eglSurface);
//
//                    LegacyCameraDevice.setNextTimestamp(holder.surface, captureHolder.second);
//                    drawFrame(mSurfaceTexture, holder.width, holder.height,
//                            (mFacing == CameraCharacteristics.LENS_FACING_FRONT) ?
//                                    FLIP_TYPE_HORIZONTAL : FLIP_TYPE_NONE);
//                    swapBuffers(holder.eglSurface);
//                } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
//                    Log.w(TAG, "Surface abandoned, dropping frame. ", e);
//                    request.setOutputAbandoned();
//                }
//            }
//        }
//        targetCollector.previewProduced();
//
//        if (doTiming) {
//            endGlTiming();
//        }
    }

    /**
     * Clean up the current GL context.
     */
    public void cleanupEGLContext() {
        releaseEGLContext();
    }

    /**
     * Drop all current GL operations on the floor.
     */
    public void flush() {
        // TODO: implement flush
        Log.e(TAG, "Flush not yet implemented.");
    }

    public void setSharedEglContext(EGLContext sharedEglContext) {
        this.mSharedEglContext = sharedEglContext;
    }
}
